#ifdef _WIN32
#include <windows.h>
#endif

#include "OpenRTS.h"

int InitGL()
{
	glClearColor(0.0f, 0.0f, 1.0f, 0.0f); // �ern� pozad�
	glClearDepth(1.0); // Povol�me maz�n� pozad�
	glDepthFunc(GL_LESS); // Vybereme typ Depth Testu
	glEnable(GL_DEPTH_TEST); // Povol�me Depth Test
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity(); // Resetujeme projek�n� matici

	glEnable(GL_SMOOTH);
	glEnable(GL_LINE_SMOOTH);
	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);

	glViewport(0, 0, Engine::screenSettings.screenWidth,
			Engine::screenSettings.screenHeight);

	glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);

	glEnable(GL_TEXTURE_2D);

	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	return true;
}

void initRTS()
{
	console_visible = false;
	info_visible = false;
	wired = false;
	fallow = false;

	// setup shader folder
	Shader::initShaderPool();
	Textures::initAll();
	World::worldInit();

	// test exceptions
	try
	{
		Shader::sPool->getProgramID("nofile");
	}
	catch (Engine::RTS_EXCEPTION *e)
	{
		e->print();
		delete(e);
	}
	//---------------

	Camera::initCamera(&fps);

	gCon::consoleInit();

	RTSMap = new Map::Map(&fps);

	// setup terrain surface in camera
	// useful to getting y value of origin
	if (Camera::camera != NULL)
	{
		Camera::camera->setTerrain(RTSMap->getTer());
		// setup water surface high
		Camera::camera->setWaterHigh(RTSMap->getWater()->getHigh());
	}

	RTSMap->generateMap();

	GameLogic::InitResource(5000, 5000);

	gCon::con->print("RTS initialization done.");

	text = new glFont(Engine::fontSettings.fontFile);
	text->BuildFont();

	// gadgets

	Gadgets::initGui();
	int stat = Gadgets::gameGui->initScript(Engine::guiSettings.guiScript);

	if (stat == 0)
	{
		// no error, bind another data
		// register resource structure to script running in gameGui
		GameLogic::resource->bindClass(Gadgets::gameGui);
		// bind fps timer to gameGui for lua scripting
		fps.bindClass(Gadgets::gameGui);
		// push meta table to lua script API
		Gadgets::gameGui->pushLuaTable();
	}

	// run LUA _init function
	Gadgets::gameGui->_init();

	infoBar = new Gadgets::Form(450, 180);
	infoBar->setBackground("infobar.png");
	infoBar->setPos(glm::vec2(5, 52));

	for (int i = 0; i < 10; i++)
	{
		infoLabels[i] = new Gadgets::Label(200, 16);
		infoLabels[i]->setPos(glm::vec2(7, 7 + (i * 16)));

		infoBar->addItem(infoLabels[i]);
	}

	infoLabels[0]->setCaption("Camera position:");
	//--------------------

	// init unitbar
	GameLogic::initUnitBar();
	GameLogic::setVisibleUnitBar(false);
}

// Main render function ----------------------------------------------------
void DrawGLScene()
{
	//glClearColor(0.29,0.39,0.9, 0.0f);
	glClearColor(0.0, 0.0, 0.0, 0.0f);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	if (wired)
		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

	RTSMap->drawAll();

	// start shader program

	glUseProgram(0);

	glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

	glActiveTexture(GL_TEXTURE0);

	if (info_visible)
	{
		if (Camera::camera != NULL)
		{
			infoLabels[1]->setCaption(
					"X: " + convertFloat(Camera::camera->getCamPos().x));
			infoLabels[2]->setCaption(
					"Y: " + convertFloat(Camera::camera->getCamPos().y));
			infoLabels[3]->setCaption(
					"Z: " + convertFloat(Camera::camera->getCamPos().z));
			infoLabels[4]->setCaption(
					"Zoom: " + convertFloat(Camera::camera->getZoom()));
		}
		// print 3D cursor position
		infoLabels[5]->setCaption(
				"Mouse 3D X: " + convertFloat(RTSMap->getCursor()->getOgLcursor().x));
		infoLabels[6]->setCaption(
				"Mouse 3D Y: " + convertFloat(RTSMap->getCursor()->getOgLcursor().y));
		infoLabels[7]->setCaption(
				"Mouse 3D Z: " + convertFloat(RTSMap->getCursor()->getOgLcursor().z));

		infoLabels[8]->setCaption(
				"Visible objects: " + convertInt(World::counter / 2) + "/"
						+ convertInt(RTSMap->getEntities()->getEntityCount()));

		infoLabels[9]->setCaption(
				"Visible parts of terrain: " + convertInt(World::countBoxesVisible / 2)
						+ "/" + convertInt(World::countBoxes));

		if (fallow)
			text->glPrint(Engine::screenSettings.screenWidth - 210, 0, 0,
					"Tracing Mode!!!");

		infoBar->draw();
	}

	// gadgets
	Gadgets::gameGui->_update();
	Gadgets::gameGui->draw();

	GameLogic::drawUnitBar();
	// -----------------------------

	if (console_visible)
		gCon::con->show();

	SDL_GL_SwapBuffers();
}
//----------------------------------------------------------------------------

// SDL inicialization
int initSDL()
{
	SDL_version compiled;

	SDL_VERSION(&compiled);
	printf("SDL: We compiled against SDL version %d.%d.%d\n",
	       compiled.major, compiled.minor, compiled.patch);


	//Initialize SDL
	if (SDL_Init(SDL_INIT_EVERYTHING) < 0)
	{
		std::cerr << "SDL: Unable to init SDL" << std::endl;
		return 1;
	}

	Uint32 flags;

	if (Engine::screenSettings.fullscreen)
		flags = SDL_OPENGL | SDL_FULLSCREEN;
	else
		flags = SDL_OPENGL;

	SDL_EnableUNICODE (SDL_TRUE);

  SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 5 );
  SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 5 );
  SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 5 );
  SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 16 );
	SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
	SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);


	//Create Window
	if (SDL_SetVideoMode(Engine::screenSettings.screenWidth,
			Engine::screenSettings.screenHeight, Engine::screenSettings.screenBpp,
			flags) == NULL)
	{
		std::cerr << "SDL: Unable to set SDL video mode" << std::endl;
		return 1;
	}

	//Initialize OpenGL
	InitGL();

	//Set caption
	SDL_WM_SetCaption("RTS Engine", NULL);

	GLeeInit();

	std::cout << "OPENGL: OpenGl version is " << glGetString(GL_VERSION) << std::endl;
	std::cout << "OPENGL: Suported GLSL versions: "
			<< glGetString(GL_SHADING_LANGUAGE_VERSION) << std::endl;

	int i;
	glGetIntegerv(GL_MAX_TEXTURE_UNITS, &i);

	std::cout << "OPENGL: Maximum count of texture layers: " << i << std::endl;

	quit = false;

	return 0;
}

void goToSelectedUnit()
{
	Entity* ent;

	if (RTSMap->getCursor()->getUnitMark().size() > 0)
	{
		ent = RTSMap->getCursor()->getUnitMark()[0];

		glm::vec2 pos;
		pos.x = ent->getPos().x;
		pos.y = ent->getPos().z;

		if (Camera::camera != NULL)
			Camera::camera->lookAt(pos);
	}
	else
		fallow = false;
}

int handleEvents()
{

	//std::cout << dClock << std::endl;

	while (SDL_PollEvent(&event))
	{
		if (Camera::camera != NULL)
			Camera::camera->control(&event);

		RTSMap->control(&event, 0);

		switch (event.type)
		{

		case SDL_KEYDOWN:
			if (event.type == SDL_KEYDOWN) // vyhodnoceni stiskle klavesy
			{

				if (!console_visible)
				{
					glm::vec3 lp;

					switch (event.key.keysym.sym)
					{
					case SDLK_f:
						if (fallow)
							fallow = false;
						else
							fallow = true;

						goToSelectedUnit();
						break;

					case SDLK_q:
						lp = World::world->getLightPos();

						lp.z = lp.z - 5.0;

						World::world->setLightPos(lp);
						World::world->update();
						break;

					case SDLK_e:
						lp = World::world->getLightPos();

						lp.z = lp.z + 5.0;

						World::world->setLightPos(lp);
						World::world->update();
						break;

					case SDLK_ESCAPE:
						quit = true;
						break;

					case SDLK_BACKQUOTE:
						console_visible = true;
						break;

					default:
						break;
					}
				}
				else
				{
					switch (event.key.keysym.sym)
					{
					case SDLK_BACKQUOTE:
						console_visible = false;
						break;
					default:
						break;
					}

					if ((command = gCon::con->get_line(event)) == C_EXIT)
					{
						console_visible = false;
						quit = true;
					}

					if (command == 4)
					{
						gCon::con->print("");
						gCon::con->print("Graphic options:");
						gCon::con->print("%i x %i color depth %i bit",
								Engine::screenSettings.screenWidth,
								Engine::screenSettings.screenHeight,
								Engine::screenSettings.screenBpp);
						gCon::con->print("");

					}

					if (command == 5)
					{
						gCon::con->print("Generating new high map.");
						RTSMap->generateMap();

						gCon::con->print("Map generated...");
					}

					if (command == 6) // show info
					{
						if (info_visible)
						{
							info_visible = false;
							gCon::con->print("Info is hidden.");
						}
						else
						{
							info_visible = true;
							gCon::con->print("Info is visible.");
						}
					}

					if (command == 7) // change terrain visibility
					{
						if (RTSMap->isTerrainVisible())
						{
							RTSMap->hideTerrain();
							gCon::con->print("Now terrain is hidden.");
						}
						else
						{
							RTSMap->showTerrain();
							gCon::con->print("Now terrain is visible.");
						}
					}

					if (command == 8) // change water visibility
					{
						if (RTSMap->isWaterVisible())
						{
							RTSMap->hideWater();
							gCon::con->print("Now water is hidden.");
						}
						else
						{
							RTSMap->showWater();
							gCon::con->print("Now water is visible.");
						}
					}

					if (command == 9) // info models
					{
						gCon::con->print("");
						gCon::con->print("Loaded models:");
						gCon::con->print(" Count: %d", RTSMap->getPool()->getCount());

						for (int i = 0; i < RTSMap->getPool()->getCount(); i++)
							gCon::con->print(" ID: %d  Name:  %s", i,
									RTSMap->getPool()->getModels()[i]->getFName().c_str());
					}

					if (command == 10) // wired terrain
					{
						if (wired)
						{
							wired = false;
							gCon::con->print("Switched to fill mode.");
						}
						else
						{
							wired = true;
							gCon::con->print("Switched to wireframe mode.");
						}
					}

					if (command == 11) // play mode
					{
						RTSMap->getCursor()->setMode(MODE_PLAY);
						gCon::con->print("Play mode active");
					}

					if (command == 13) // play mode
					{
						RTSMap->getCursor()->setMode(MODE_EDIT);
						gCon::con->print("Edit mode active");
					}

					if (command == 14) // wired terrain
					{
						if (RTSMap->isBoxVisible())
						{
							RTSMap->setBoxVisible(false);
							gCon::con->print("Bounding boxes hidden.");
						}
						else
						{
							RTSMap->setBoxVisible(true);
							gCon::con->print("Bounding boxes visible.");
						}
					}

					if (command == 15) // wired terrain
					{
						if (RTSMap->isOctreeBox())
						{
							RTSMap->setOctreeBox(false);
							gCon::con->print("Terrain boxes hidden.");
						}
						else
						{
							RTSMap->setOctreeBox(true);
							gCon::con->print("Terrain boxes visible.");
						}
					}

					command = -1;
				}

			}
			break;

		case SDL_QUIT:
			quit = true;
			break;
			//exit(0);
		}
	}

	return 0;
}

// main loop of SDL ------------------------------------------------------------------------
void SDLMainLoop()
{

	while (quit == false)
	{
		if (Camera::camera != NULL)
			Camera::camera->update();

		handleEvents();

		if (fallow)
			goToSelectedUnit();

		World::countBoxesVisible = 0;

		//Render frame
		DrawGLScene();

		// set counter back to 0
		World::counter = 0;
	}
}
//----------------------------------------------------------------------------------------------

void clean()
{
	// stop thread
	ThrPool::quitEntityThread();

	text->KillFont();
	delete (text);
	Camera::deleteCamera();
	delete (RTSMap);

	gCon::deleteCon();
	World::deleteWorld();

	delete (infoBar);

	Gadgets::deleteGui();

	Textures::deleteAll();
}

int main(int argc, char **argv)
{
	// info
	std::cout
			<< "\nOpenRTS: engine running\n"
			<< std::endl;

#ifdef _DEBUG
	std::cout << "OpenRTS: debug mode is on\n" << std::endl;
#else
	std::cout << "OpenRTS: debug mode is off\n" << std::endl;
#endif
	Engine::loadSettings("settings.xml", RTS_LOAD_ALL);

	if (initSDL())
		return 1;

	initRTS();

	SDLMainLoop();

	//Clean up
	clean();

	std::cout << "\nOpenRTS: OpenRTS DONE\n"
			<< std::endl;
	SDL_Quit();
	return 0;
}
